package cz.data.domain.market.mapping.config;

import cz.data.common.security.AbstractRequestMatcher;
import cz.data.common.security.InMemoryRequestMatcherRepository;
import cz.data.common.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@EnableAsync
@Configuration
public class AppConfig {

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setKeepAliveSeconds(30);
        executor.setThreadNamePrefix("data-async-thread");
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Bean
    public InMemoryRequestMatcherRepository matcherRepository() {
        return new InMemoryRequestMatcherRepository(this::createMvcMatchers);
    }

    @Order(3)
    @Bean
    @ConditionalOnBean(InMemoryRequestMatcherRepository.class)
    public SecurityFilterChain dynamicMappingSecurityFilterChain(
            HttpSecurity http, AbstractRequestMatcher requestMatcher) throws Exception {
        log.info("加载动态mapping安全配置");
        return http
                .requestMatcher(requestMatcher)
                .authorizeRequests(request -> request.anyRequest().permitAll())
                .csrf(CsrfConfigurer::disable)
                .formLogin(FormLoginConfigurer::disable)
                .rememberMe(RememberMeConfigurer::disable)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .build();
    }

    private MvcRequestMatcher createMvcMatchers(String requestMethod, String mvcPattern) {
        HttpMethod method = HttpMethod.resolve(requestMethod);
        ObjectPostProcessor<Object> opp = SpringContextHolder.getBean(ObjectPostProcessor.class);
        HandlerMappingIntrospector introspector = SpringContextHolder.getBean(HandlerMappingIntrospector.class);
        MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
        opp.postProcess(matcher);
        if (method != null) {
            matcher.setMethod(method);
        }
        return matcher;
    }
}
